Cypress v6 から Fetch による API 呼び出しがデフォルトで監視できるようになっていた
はじめに
テントの中から失礼します、CX事業本部のてんとタカハシです!
E2E テストを実装する際、API のレスポンスを待ってから、何かの要素をチェックしたいケースがあります。Cypress では、Fetch による API の呼び出しがデフォルトで監視できず、別途オプションを付け足す必要があったのですが、Version 6 からはそれが不要になったようです。
テスト対象
例えば、従業員のデータを API から取得して、リストを作成するページがあったとします。API の呼び出しには Fetch を使用しています。
import React, { FC, useEffect, useState } from 'react'; import './App.css'; type Employee = { id: number; name: string; age: number; }; const App: FC = () => { const [employees, setEmployees] = useState<Employee[]>([]); const listStyle = { marginBottom: '4px', }; useEffect(() => { fetch('http://localhost:3001/employees') .then((response) => response.json()) .then((data) => setEmployees(data)); }, []); return ( <div className="App"> <h3>Employees</h3> <ul id="employees"> {employees.map((employee) => ( <li key={employee.id} style={listStyle}> {employee.name} </li> ))} </ul> </div> ); }; export default App;
UI はこんな感じです。
API を呼び出した後、リストが作成されているか確認する E2E テストを実装するとします。
今まで(v4.9 ~ v5系)
失敗例
下記の実装で、/employees
のレスポンスを待ってから、リストが作成されているか確認できるのですが、
describe('サンプルテスト', () => { beforeEach(() => { cy.server(); cy.route('GET', '/employees').as('employees'); }); it('Employees API のレスポンスを待つ', () => { cy.visit('/'); cy.wait('@employees'); cy.get('#employees > li').should('have.length', 5); }); });
このまま E2E テストを実行しても失敗に終わります。
$ cypress run Missing baseUrl in compilerOptions. tsconfig-paths will be skipped ==================================================================================================== (Run Starting) ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Cypress: 5.6.0 │ │ Browser: Electron 85 (headless) │ │ Specs: 1 found (sample_spec.js) │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ──────────────────────────────────────────────────────────────────────────────────────────────────── Running: sample_spec.js (1 of 1) サンプルテスト 1) Employees API のレスポンスを待つ 0 passing (6s) 1 failing 1) サンプルテスト Employees API のレスポンスを待つ: CypressError: Timed out retrying: `cy.wait()` timed out waiting `5000ms` for the 1st request to the route: `employees`. No request ever occurred. https://on.cypress.io/wait at cypressErr (http://localhost:3000/__cypress/runner/cypress_runner.js:172713:18) at Object.errByPath (http://localhost:3000/__cypress/runner/cypress_runner.js:172764:10) at checkForXhr (http://localhost:3000/__cypress/runner/cypress_runner.js:159916:33) at http://localhost:3000/__cypress/runner/cypress_runner.js:159941:28 at tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:10325:23) at Function.Promise.attempt.Promise.try (http://localhost:3000/__cypress/runner/cypress_runner.js:7599:29) at tryFn (http://localhost:3000/__cypress/runner/cypress_runner.js:165556:21) at whenStable (http://localhost:3000/__cypress/runner/cypress_runner.js:165594:12) at http://localhost:3000/__cypress/runner/cypress_runner.js:165089:16 at tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:10325:23) at Promise._settlePromiseFromHandler (http://localhost:3000/__cypress/runner/cypress_runner.js:8260:31) at Promise._settlePromise (http://localhost:3000/__cypress/runner/cypress_runner.js:8317:18) at Promise._settlePromise0 (http://localhost:3000/__cypress/runner/cypress_runner.js:8362:10) at Promise._settlePromises (http://localhost:3000/__cypress/runner/cypress_runner.js:8442:18) at Promise._fulfill (http://localhost:3000/__cypress/runner/cypress_runner.js:8386:18) at http://localhost:3000/__cypress/runner/cypress_runner.js:10000:46 From Your Spec Code: at Context.eval (http://localhost:3000/__cypress/tests?p=cypress/integration/sample_spec.js:106:8) (Results) ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Tests: 1 │ │ Passing: 0 │ │ Failing: 1 │ │ Pending: 0 │ │ Skipped: 0 │ │ Screenshots: 1 │ │ Video: true │ │ Duration: 5 seconds │ │ Spec Ran: sample_spec.js │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ (Screenshots) - /Users/hogehoge/fugafuga/piyopiyo/cypress/screenshots (1280x720) /sample_spec.js/サンプルテスト -- Employees API のレスポンスを待つ (fai… (Video) - Started processing: Compressing to 32 CRF - Finished processing: /Users/hogehoge/fugafuga/piyopiyo/cypress (0 seconds) /videos/sample_spec.js.mp4 ==================================================================================================== (Run Finished) Spec Tests Passing Failing Pending Skipped ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ ✖ sample_spec.js 00:05 1 - 1 - - │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ✖ 1 of 1 failed (100%) 00:05 1 - 1 - -
cy.wait()
した際に、API の呼び出しを監視できていなく、そのままタイムアウトしてしまうようです。
CypressError: Timed out retrying: `cy.wait()` timed out waiting `5000ms` for the 1st request to the route: `employees`. No request ever occurred.
成功例
Cypress では、v4.9 より Fetch による API の呼び出しを監視するためのオプションが追加されました。
For a long, long, loooong time, the Cypress network control could not "see" window.fetch calls and only understood XMLHttpRequest Ajax calls.
...
Meanwhile, we have added a quick fetch polyfill as an experimental feature in Cypress v4.9.0. By turning this feature on, the Cypress Test Runner will automatically replace window.fetch with a unfetch polyfill built on top of XMLHttpRequest object, making these Ajax requests "visible" to the Test Runner.
Cypress - experimental-fetch-polyfill
cypress.json
に下記を追加するだけで OK です。
{ ... "experimentalFetchPolyfill": true }
これで E2E テストが成功するようになります。
$ cypress run Missing baseUrl in compilerOptions. tsconfig-paths will be skipped ==================================================================================================== (Run Starting) ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Cypress: 5.6.0 │ │ Browser: Electron 85 (headless) │ │ Specs: 1 found (sample_spec.js) │ │ Experiments: experimentalFetchPolyfill=true │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ──────────────────────────────────────────────────────────────────────────────────────────────────── Running: sample_spec.js (1 of 1) サンプルテスト ✓ Employees API のレスポンスを待つ (381ms) 1 passing (409ms) (Results) ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Tests: 1 │ │ Passing: 1 │ │ Failing: 0 │ │ Pending: 0 │ │ Skipped: 0 │ │ Screenshots: 0 │ │ Video: true │ │ Duration: 0 seconds │ │ Spec Ran: sample_spec.js │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ (Video) - Started processing: Compressing to 32 CRF - Finished processing: /Users/hogehoge/fugafuga/piyopiyo (0 seconds) ==================================================================================================== (Run Finished) Spec Tests Passing Failing Pending Skipped ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ ✔ sample_spec.js 404ms 1 1 - - - │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ✔ All specs passed! 404ms 1 1 - - -
v6 から
Version 6 からは intercept()
を使用することで、Fetch による API 呼び出しを監視できるようになりました。cypress.json
にオプションを付け足す必要はもうありません。
・ experimentalFetchPolyfill has been deprecated. We encourage you to use cy.intercept() to intercept requests using the Fetch API instead.
・ can intercept all types of network requests including Fetch API, page loads, XMLHttpRequests, resource loads, etc.
実装は下記になります。
describe('サンプルテスト', () => { beforeEach(() => { cy.intercept({ url: '/employees', method: 'GET', }).as('employees'); }); it('Employees API のレスポンスを待つ', () => { cy.visit('/'); cy.wait('@employees'); cy.get('#employees > li').should('have.length', 5); }); });
ちゃんと E2E テストが成功しました。
$ cypress run Missing baseUrl in compilerOptions. tsconfig-paths will be skipped ==================================================================================================== (Run Starting) ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Cypress: 6.0.1 │ │ Browser: Electron 87 (headless) │ │ Specs: 1 found (sample_spec.js) │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ──────────────────────────────────────────────────────────────────────────────────────────────────── Running: sample_spec.js (1 of 1) サンプルテスト ✓ Employees API のレスポンスを待つ (369ms) 1 passing (404ms) (Results) ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Tests: 1 │ │ Passing: 1 │ │ Failing: 0 │ │ Pending: 0 │ │ Skipped: 0 │ │ Screenshots: 0 │ │ Video: true │ │ Duration: 0 seconds │ │ Spec Ran: sample_spec.js │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ (Video) - Started processing: Compressing to 32 CRF - Finished processing: /Users/hogehoge/fugafuga/piyopiyo (0 seconds) ==================================================================================================== (Run Finished) Spec Tests Passing Failing Pending Skipped ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ ✔ sample_spec.js 385ms 1 1 - - - │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ ✔ All specs passed! 385ms 1 1 - - -
おわりに
Cypress のちょいネタでした。Fetch による API 呼び出しを行う際の、ちょっとした躓きポイントが解消されましたね。
今回は以上になります。最後まで読んで頂きありがとうございました!